﻿using System;
using System.Net;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using BrettM.Web.Restful;
using Microsoft.Practices.Unity;

namespace BrettM.Web.Hosting
{
    /// <summary>
    /// The RestHttpApplication inherits from HttpApplication and provides the bootstrapping
    /// requirements of the RESTful service.
    /// </summary>
    public class RestHttpApplication
        : HttpApplication
    {
        private bool _disposed = false;
        private readonly PropertyBag _propertyBag;

        /// <summary>
        /// The created unity container for dependency injection
        /// </summary>
        protected IUnityContainer UnityContainer { get; private set; }

        /// <summary>
        /// The default ctor
        /// </summary>
        public RestHttpApplication()
        {
            _propertyBag = new PropertyBag();
        }

        /// <summary>
        /// The dtor which ensures closure of the bootstrappers
        /// </summary>
        ~RestHttpApplication()
        {
            if (_disposed)
            {
                ShutDown();
            }
        }


        /// <summary>
        /// Creates an empty container
        /// </summary>
        /// <returns></returns>
        public virtual IUnityContainer OnCreateContainer()
        {
            return new UnityContainer();
        }

        /// <summary>
        /// Handles all exceptions thrown on the server, redirecting to the error controller.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void Application_Error(object sender, EventArgs e)
        {
            // Get the last server error, exit if none
            var objErr = Server.GetLastError();
            if (objErr == null) return;

            var errorHandlers = ErrorHandler.ErrorHandlers;
            var errorEventArgs = new ErrorEventArgs(objErr);

            foreach (var errorHandler in errorHandlers)
            {
                if (errorHandler.CanHandleError(this, errorEventArgs))
                {
                    errorHandler.OnError(this, errorEventArgs);
                    break;
                }
            }
        }


        /// <summary>
        /// Called at the start of the service when global.asax is initialized and is
        /// responsible for kickstarting the boot strapping process
        /// </summary>
        protected void Application_Start(object sender, EventArgs e)
        {
            // Create the dependency injection container for the life of the application
            // If none is created then asume the default
            UnityContainer = OnCreateContainer() ?? new UnityContainer();

            // Resolve all registered bootstrappers and start them up
            foreach (var bootStrapper in UnityContainer.ResolveAll<IBootStrapper>())
            {
                try
                {
                    bootStrapper.OnStart(UnityContainer, _propertyBag);
                }
                finally
                {
                    bootStrapper.Dispose();
                }
            }
        }

        /// <summary>
        /// Ensures that the bootstrappers loaded are shutdown passing in their
        /// propertybags
        /// </summary>
        private void ShutDown()
        {
            if (_disposed) return;

            // Loop through all bootstrappers and call their OnStop.
            foreach (var bootStrapper in UnityContainer.ResolveAll<IBootStrapper>())
            {
                try
                {
                    bootStrapper.OnStop(UnityContainer, _propertyBag);
                }
                finally
                {
                    bootStrapper.Dispose();
                }
                
            }
            // Clear the property bag
            _propertyBag.Clear();
            // Indicate that this all resources ready to dispose
            _disposed = true;
        }

        /// <summary>
        /// HttpApplication is Disposable, this virtual method intercepts the dispose,
        /// calls a shutdown and calls the base dispose.
        /// </summary>
        public override void Dispose()
        {
            ShutDown();
            base.Dispose();
        }
    }
}
